package com.sokobanboxpro.game.gameplay;

import com.sokobanboxpro.game.exception.ConfigValueNotFoundException;
import com.sokobanboxpro.game.managers.GraphicsManager;
import com.sokobanboxpro.game.managers.LevelManager;
import com.sokobanboxpro.game.managers.SaveGameStateManager;
import com.sokobanboxpro.game.system.Config;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;

/**
 * Игра. Игра - одно из состояний приложения, содержит в себе геймплей и
 * интерфейс Определяет, какой сечас уровень играется
 *
 * @author Esr
 */
public class Game {

		/**
		 * Текущий уровень/
		 */
		private Level currentLevel;
		/**
		 * Номер текущего уровня.
		 */
		private int currentLevelNumber;
		/**
		 * Геймплей игры.
		 */
		private Gameplay gameplay;
		/**
		 * Интерфейс игры.
		 */
		private Interface inter;
		/**
		 * Количество пройденных уровней.
		 */
		private int levelsComplite;
		/**
		 * Оставшееся количество пропусков.
		 */
		private int skips;
		/**
		 * Пропушенные уровни.
		 */
		private ArrayList<Integer> skipsLevels = new ArrayList<Integer>();

		/**
		 * Конструктор, ничего не делает.
		 */
		public Game() {
		}

		/**
		 * Инициализация игры. Загружаються уровни. Создается геймплей и интерфейс
		 *
		 * @throws SlickException
		 * @throws IOException Файл уровней не может быть открыт
		 * @throws ConfigValueNotFoundException Не найден путь до уровней
		 * @throws Exception
		 */
		public void init() throws SlickException, IOException, ConfigValueNotFoundException, Exception {

				gameplay = new Gameplay();
				inter = new Interface();

				inter.init(this);
				gameplay.init(this);

				LevelManager.loadLevelFromFile(Config.toStr("LevelPath"));
				setCurrentLevel(LevelManager.getLevel(0));

				SaveGameStateManager.loadState();

				setLevelsComplite(SaveGameStateManager.getLevelsComplite());

				skips = SaveGameStateManager.getSkips();
				skipsLevels = SaveGameStateManager.getSkipsLevel();

		}

		/**
		 * Нажатие мыши или касание экрана.
		 *
		 * @param x Координата x
		 * @param y Координата y
		 */
		public void mousePressed(int x, int y) throws Exception {

				if (y > 700) {
						gameplay.mousePressed(x, y);
				} else {
						inter.mousePressed(x, y);
				}

		}

		/**
		 * Получить текущий уровень.
		 *
		 * @return Текущий уровень
		 */
		public Level getCurrentLevel() {
				return currentLevel;
		}

		/**
		 * Сменить уровень.
		 *
		 * @param num Номер уровня
		 */
		public void changeLevel(int num) throws Exception {
				if (LevelManager.levelAmount() <= num) {
						throw new Exception("Такого уровня нету");
				}
				currentLevelNumber = num;
				setCurrentLevel(LevelManager.getLevel(num));
		}

		/**
		 * Перейти на следующий уровень.
		 */
		public void changeLevelUp() {
				int num = currentLevelNumber + 1;
				if (LevelManager.levelAmount() > num && num < getLevelsComplite()) {
						currentLevelNumber = num;
						setCurrentLevel(LevelManager.getLevel(num));
				}
		}

		/**
		 * Перейти на уровень ниже.
		 */
		public void changeLevelDown() {
				int num = currentLevelNumber - 1;
				if (num >= 0) {
						currentLevelNumber = num;
						setCurrentLevel(LevelManager.getLevel(num));
				}

		}

		/**
		 * Заменить уровень.
		 *
		 * @param level Уровень, который будет установлен
		 */
		private void setCurrentLevel(Level level) {
				gameplay.levelChanged();
				inter.levelChanged();
				this.currentLevel = level;
				int height = level.getHeight() * GraphicsManager.getHeightBlock();
				int width = level.getWidth() * GraphicsManager.getWidthBlock();

				float kHeight = 1, kWidth = 1;

				if (height > 480) {
						kHeight = 480 / height;
				}
				if (width > 700) {
						kWidth = 700 / width;
				}

				gameplay.getCamera().setZoom(Math.min(kWidth, kHeight));

				gameplay.getCamera().setX((700 - (width * Math.min(kWidth, kHeight))) / 2);
				gameplay.getCamera().setY((480 - (height * Math.min(kWidth, kHeight))) / 2);



		}

		/**
		 * Получить геймплей.
		 *
		 * @return Геймплей
		 */
		public Gameplay getGameplay() {
				return gameplay;
		}

		/**
		 * Получить интерфейс.
		 *
		 * @return Интерфейс
		 */
		public Interface getInterface() {
				return inter;
		}

		/**
		 * Номер текущего уроня.
		 *
		 * @return Номер текущего уроня
		 */
		public int getCurrentLevelNumber() {
				return currentLevelNumber;
		}

		/**
		 * Функция, вызываемая при смене уровней.
		 */
		public void levelComlited() {
				int num = currentLevelNumber + 1;
				if (existSkipLevel(num)) {

						delSkipLevel(num);
						skips += 1;
						SaveGameStateManager.setSkips(skips);
				}

				if (num == levelsComplite) {
						setLevelsComplite(levelsComplite + 1);
				}

				changeLevelUp();

				try {
						SaveGameStateManager.saveState();
				} catch (UnsupportedEncodingException ex) {
						Log.error("Не возможная кодировка " + ex);
						ex.printStackTrace();

				} catch (FileNotFoundException ex) {
						Log.error("Файл не был найден " + ex);
						ex.printStackTrace();
				}

		}

		/**
		 * Рестарт уровня.
		 */
		public void resetLevel() {
				currentLevel = LevelManager.getLevel(currentLevelNumber);
		}

		/**
		 * Получить количество пройденных уровней.
		 *
		 * @return Количество пройденных уровней
		 */
		public int getLevelsComplite() {
				return levelsComplite;
		}

		/**
		 * Установить количество пройденных уровней.
		 *
		 * @param levelsComplite Количество пройденных уровней
		 */
		public void setLevelsComplite(int levelsComplite) {

				this.levelsComplite = levelsComplite;
				SaveGameStateManager.setLevelsComplite(levelsComplite);


		}

		/**
		 * Получить количество оставшихся пропусков уровней.
		 *
		 * @return Количество оставшихся пропусков уровней
		 */
		public int getSkips() {
				return skips;
		}

		/**
		 * Установить количество оставшихся пропусков уровней.
		 *
		 * @param skips Количество оставшихся пропусков уровней
		 */
		public void setSkips(int skips) {
				this.skips = skips;

				SaveGameStateManager.setSkips(skips);
				try {
						SaveGameStateManager.saveState();
				} catch (UnsupportedEncodingException ex) {
						Log.error("Не возможная кодировка " + ex);
						ex.printStackTrace();

				} catch (FileNotFoundException ex) {
						Log.error("Файл не был найден " + ex);
						ex.printStackTrace();
				}
		}

		/**
		 * Пропустить текущий уровен.
		 */
		public void skipLevel() {

				if (skips != 0) {
						skips -= 1;
						if (levelsComplite != LevelManager.levelAmount()) {
								levelsComplite += 1;
						}
						addSkipLevel(levelsComplite - 1);
						SaveGameStateManager.setSkipsLevel(skipsLevels);
						SaveGameStateManager.setLevelsComplite(levelsComplite);
						SaveGameStateManager.setSkips(skips);
						try {
								SaveGameStateManager.saveState();
						} catch (UnsupportedEncodingException ex) {
								Log.error("Не возможная кодировка " + ex);
								ex.printStackTrace();

						} catch (FileNotFoundException ex) {
								Log.error("Файл не был найден " + ex);
								ex.printStackTrace();
						}
				}
		}

		/**
		 * Добавить уровенб к пропушеным.
		 *
		 * @param levelNumber Номер уровня
		 */
		private void addSkipLevel(Integer levelNumber) {

				skipsLevels.add(levelNumber);

		}

		/**
		 * Удалить уровень из пропушенных.
		 *
		 * @param levelNumber Номер уровня
		 */
		private void delSkipLevel(Integer levelNumber) {
				skipsLevels.remove(levelNumber);
		}

		/**
		 * Установить, пропускался ли уровень.
		 *
		 * @param levelNumber Номер уровня
		 * @return Пропускался ли
		 */
		private boolean existSkipLevel(Integer levelNumber) {

				return skipsLevels.contains(levelNumber);

		}

}
